import _ from 'lodash'

/**
 * WebSocket 核心管理器
 * 提供基础的 WebSocket 连接管理和消息路由功能
 */
export class WebSocketManager {
  constructor(logger) {
    this.logger = logger
    this.clients = new Map()        // 客户端连接
    this.rooms = new Map()          // 房间管理
    this.handlers = new Map()       // 消息处理器
    this.middleware = []            // 中间件
  }
  
  // === 连接管理 ===
  
  addClient(ws, id = null) {
    const clientId = id || this.generateId()
    const client = {
      id: clientId,
      ws,
      rooms: new Set(),
      data: {},
      connectedAt: Date.now()
    }
    
    this.clients.set(clientId, client)
    this.logger.debug('WebSocketManager', `Client connected: ${clientId}`)
    
    return clientId
  }
  
  removeClient(clientId) {
    const client = this.clients.get(clientId)
    if (!client) return
    
    // 从所有房间移除
    client.rooms.forEach(roomId => {
      this.leave(clientId, roomId)
    })
    
    this.clients.delete(clientId)
    this.logger.debug('WebSocketManager', `Client disconnected: ${clientId}`)
  }
  
  getClient(clientId) {
    return this.clients.get(clientId)
  }
  
  getAllClients() {
    return Array.from(this.clients.values())
  }
  
  // === 房间管理 ===
  
  join(clientId, roomId) {
    const client = this.clients.get(clientId)
    if (!client) return false
    
    if (!this.rooms.has(roomId)) {
      this.rooms.set(roomId, new Set())
    }
    
    this.rooms.get(roomId).add(clientId)
    client.rooms.add(roomId)
    
    this.logger.debug('WebSocketManager', `Client ${clientId} joined room ${roomId}`)
    return true
  }
  
  leave(clientId, roomId) {
    const client = this.clients.get(clientId)
    const room = this.rooms.get(roomId)
    
    if (client) {
      client.rooms.delete(roomId)
    }
    
    if (room) {
      room.delete(clientId)
      if (room.size === 0) {
        this.rooms.delete(roomId)
      }
    }
    
    this.logger.debug('WebSocketManager', `Client ${clientId} left room ${roomId}`)
  }
  
  getRoomClients(roomId) {
    const room = this.rooms.get(roomId)
    return room ? Array.from(room) : []
  }
  
  // === 消息发送 ===
  
  send(clientId, event, data = {}) {
    const client = this.clients.get(clientId)
    if (!client) return false
    
    try {
      const message = JSON.stringify({ event, data, timestamp: Date.now() })
      client.ws.send(message)
      return true
    } catch (error) {
      this.logger.error('WebSocketManager', `Failed to send to ${clientId}:`, { error: error.message })
      return false
    }
  }
  
  broadcast(event, data = {}) {
    const message = JSON.stringify({ event, data, timestamp: Date.now() })
    let sent = 0
    
    this.clients.forEach(client => {
      try {
        client.ws.send(message)
        sent++
      } catch (error) {
        this.logger.error('WebSocketManager', `Broadcast failed for ${client.id}:`, { error: error.message })
      }
    })
    
    this.logger.debug('WebSocketManager', `Broadcast ${event} to ${sent}/${this.clients.size} clients`)
    return sent
  }
  
  broadcastToRoom(roomId, event, data = {}, excludeClient = null) {
    const room = this.rooms.get(roomId)
    if (!room) return 0
    
    const message = JSON.stringify({ event, data, timestamp: Date.now() })
    let sent = 0
    
    room.forEach(clientId => {
      if (clientId === excludeClient) return
      
      const client = this.clients.get(clientId)
      if (client) {
        try {
          client.ws.send(message)
          sent++
        } catch (error) {
          this.logger.error('WebSocketManager', `Room broadcast failed for ${clientId}:`, { error: error.message })
        }
      }
    })
    
    this.logger.debug('WebSocketManager', `Broadcast ${event} to room ${roomId}: ${sent} clients`)
    return sent
  }
  
  // === 消息处理 ===
  
  on(event, handler) {
    if (!this.handlers.has(event)) {
      this.handlers.set(event, [])
    }
    this.handlers.get(event).push(handler)
  }
  
  off(event, handler) {
    const handlers = this.handlers.get(event)
    if (handlers) {
      const index = handlers.indexOf(handler)
      if (index > -1) {
        handlers.splice(index, 1)
      }
    }
  }
  
  use(middleware) {
    this.middleware.push(middleware)
  }
  
  async handleMessage(clientId, message) {
    let parsed
    
    try {
      parsed = JSON.parse(message)
    } catch (error) {
      this.logger.warn('WebSocketManager', `Invalid JSON from ${clientId}:`, { message })
      return
    }
    
    const { event, data = {} } = parsed
    
    // 执行中间件
    for (const mw of this.middleware) {
      const result = await mw(clientId, event, data, this)
      if (result === false) {
        this.logger.debug('WebSocketManager', `Middleware blocked ${event} from ${clientId}`)
        return
      }
    }
    
    // 执行处理器
    const handlers = this.handlers.get(event) || []
    const wildcardHandlers = this.handlers.get('*') || []
    const allHandlers = [...handlers, ...wildcardHandlers]
    
    if (allHandlers.length === 0) {
      this.logger.warn('WebSocketManager', `No handler for event: ${event}`)
      return
    }
    
    for (const handler of allHandlers) {
      try {
        await handler(clientId, data, this)
      } catch (error) {
        this.logger.error('WebSocketManager', `Handler error for ${event}:`, { error: error.message })
      }
    }
  }
  
  // === WebSocket 事件处理（供 Bun.serve 使用）===
  
  onOpen(ws) {
    const clientId = this.addClient(ws)
    ws.data = { clientId }
    
    // 发送欢迎消息
    this.send(clientId, 'connected', { clientId })
  }
  
  onMessage(ws, message) {
    const clientId = ws.data?.clientId
    if (!clientId) {
      this.logger.error('WebSocketManager', 'Message from unknown client')
      return
    }
    
    this.handleMessage(clientId, message)
  }
  
  onClose(ws) {
    const clientId = ws.data?.clientId
    if (clientId) {
      this.removeClient(clientId)
    }
  }
  
  onError(ws, error) {
    const clientId = ws.data?.clientId
    this.logger.error('WebSocketManager', `WebSocket error for ${clientId}:`, { error: error.message })
  }
  
  // === 工具方法 ===
  
  generateId() {
    return `ws_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
  }
  
  getStats() {
    return {
      clients: this.clients.size,
      rooms: this.rooms.size,
      handlers: this.handlers.size,
      middleware: this.middleware.length
    }
  }
}